//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_LINE_H
#define _UXS_LINE_H

#pragma once

#include "TVec.h"
#include "TRect.h"

namespace UX_STL
{
namespace UXMath
{


	//
	// Line
	//

	template <typename VecType>
	struct TLine
	{
	// types
	public:
		typedef TLine< VecType >			Self;
		typedef VecType						vec_t;
		typedef typename VecType::value_t	value_t;


	// variables
	private:
		vec_t	_p0;
		vec_t	_p1;


	// methods
	public:
		TLine () {}

		explicit
		TLine (const vec_t &end) : _p0(), _p1(end) {}

		TLine (const vec_t &p0, const vec_t &p1) : _p0(p0), _p1(p1) {}

		bool	operator == (const Self &right) const;
		bool	operator != (const Self &right) const;

		value_t			X1 ()			const	{ return _p0.x; }
		value_t			X2 ()			const	{ return _p1.x; }
		value_t			Y1 ()			const	{ return _p0.y; }
		value_t			Y2 ()			const	{ return _p1.y; }

		vec_t &			Begin ()				{ return _p0; }
		vec_t const &	Begin ()		const	{ return _p0; }

		vec_t &			End ()					{ return _p1; }
		vec_t const &	End ()			const	{ return _p1; }

		value_t			Length ()		const	{ return _p1.Distance( _p0 ); }
		vec_t			Vector ()		const	{ return (_p1 - _p0); }
		vec_t			Direction ()	const	{ return Vector().Normalized(); }
		vec_t			Center ()		const	{ return (Begin() + End()) * value_t(0.5); }

		Self Invert ()					const	{ return Self( _p1, _p0 ); }

		Self Move (const vec_t &delta)	const	{ return Self( _p0 + delta, _p1 + delta ); }

		void GetEquation (OUT value_t &k, OUT value_t &b)					const;
		void GetEquation (OUT value_t &a, OUT value_t &b, OUT value_t &c)	const;
		void GetBoundingRect (OUT TRect<value_t> &boundRect)				const;

		value_t	GetXfromY (value_t y) const;
		value_t	GetYfromX (value_t x) const;

		bool IsInnerPoint (const vec_t &point)		const;
		bool IsInnerLine (const Self &line)			const;

		bool IntersectLine (const Self &other)		const;
		bool IntersectRect (const TRect<value_t> &other)	const;

		bool GetVecIntersection (const Self &other, OUT vec_t &result) const;
		bool GetLineIntersection (const Self &other, OUT vec_t &result) const;
		bool GetRectIntersection (const TRect<value_t> &other, OUT vec_t &result0, OUT vec_t &result1) const;

		bool GetRayIntersection (const Self &other, OUT vec_t &res) const;

		bool IsPointInBoundingRects (const Self &other, const vec_t &point) const;

		value_t	AngleBetweenLines (const Self &sLine)	const;	// angle of other line rotation by this line in a clockwise 
		value_t	CosBetweenLines (const Self &sLine)		const;
		Self	Reflection (const vec_t &normal)		const;

		bool	PerpendicularBase (const vec_t &point, OUT vec_t &base)	const;
		bool	Perpendicular (const vec_t &point, OUT Self &result)		const;
		
		vec_t	Normal () const;
		vec_t	LeftNormal () const;
		vec_t	RightNormal () const;
		vec_t	GetInterpolationPoint (value_t value)	const	{ return Mix( _p0, _p1, value ); }


		static bool _CropLine (const Self &src, Self &dst);

	private:
		bool _IntersectRect (const TRect<value_t> &other, OUT vec_t &res0, OUT vec_t &res1) const;
	};


	
	
		
	template <typename VecType>
	inline bool	TLine<VecType>::operator == (const Self &right) const
	{
		return All( Begin() == right.Begin() ) and All( End() == right.End() );
	}
	

	template <typename VecType>
	inline bool	TLine<VecType>::operator != (const Self &right) const
	{
		return not ( (*this) == right );
	}
	

	template <typename VecType>
	inline void TLine<VecType>::GetEquation (value_t &K, value_t &B) const
	{
		// y = Kx + B

		value_t	a, b, c;
		GetEquation( a, b, c );
		
		if ( IsNotZero( b ) )
		{
			K = -a / b;
			B = -c / b;
		}
		else
		{
			K = 0;
			B = 0;
			WARNING( "!!!" );
		}
	}
		
	template <typename VecType>
	inline void TLine<VecType>::GetEquation (value_t &A, value_t &B, value_t &C) const
	{
		// Ax + By + C = 0

		STATIC_ASSERT( VecType::STATIC_COUNT == 2, "only 2D supported" );

		A = Y1() - Y2();
		B = X2() - X1();
		C = X1() * Y2() - X2() * Y1();
	}
	
		
	template <typename VecType>
	inline typename VecType::value_t  TLine<VecType>::GetXfromY (value_t y) const
	{
		// ax + by + c = 0 -> x = -(by + c) / a

		value_t	a, b, c;
		GetEquation( a, b, c );
		return SafeDiv( - (b * y + c ), a, value_t(0) );
	}


	template <typename VecType>
	inline typename VecType::value_t  TLine<VecType>::GetYfromX (value_t x) const
	{
		// y = kx + b
		value_t	k, b;
		GetEquation( k, b );
		return k * x + b;
	}

		
	template <typename VecType>
	inline bool TLine<VecType>::PerpendicularBase (const vec_t &p, OUT vec_t &base) const
	{
		STATIC_ASSERT( VecType::STATIC_COUNT == 2, "only 2D supported" );
		
		const value_t		x2_x1	 = X2() - X1();
		const value_t		y2_y1	 = Y2() - Y1();
		const value_t		x2_x1_p2 = Square( x2_x1 );
		const value_t		y2_y1_p2 = Square( y2_y1 );

		base.x = ( X1() * y2_y1_p2 + p.x * x2_x1_p2 + x2_x1 * y2_y1 * ( p.y - Y1() ) ) / ( y2_y1_p2 + x2_x1_p2 );
		base.y = ( x2_x1 * ( p.x - base.x ) ) / y2_y1 + p.y;

		return IsInnerPoint( base );
	}
	
	
	template <typename VecType>
	inline bool TLine<VecType>::Perpendicular (const vec_t &point, OUT Self &result) const
	{
		vec_t	base;

		if ( not PerpendicularBase( point, base ) )
		{
			result = Self();
			return false;
		}

		result = Self( point, base );
		return true;
	}
	
	
	template <typename VecType>
	inline VecType TLine<VecType>::Normal () const
	{
		STATIC_ASSERT( VecType::STATIC_COUNT == 2, "only 2D supported" );

		const vec_t	v = Vector();
		return ( v.x >= value_t(0) ? vec_t( -v.y, v.x ) : vec_t( v.y, -v.x ) ).Normalized();
	}
	

	template <typename VecType>
	inline VecType TLine<VecType>::LeftNormal () const
	{
		STATIC_ASSERT( VecType::STATIC_COUNT == 2, "only 2D supported" );
		const vec_t	v = Vector();
		return vec_t( -v.y, v.x ).Normalized();
	}
	

	template <typename VecType>
	inline VecType TLine<VecType>::RightNormal () const
	{
		STATIC_ASSERT( VecType::STATIC_COUNT == 2, "only 2D supported" );
		const vec_t	v = Vector();
		return vec_t( v.y, -v.x ).Normalized();
	}


	template <typename VecType>
	inline void TLine<VecType>::GetBoundingRect (TRect<value_t> &boundRect) const
	{
		boundRect.FromPoints( Begin(), End() );
		boundRect.Stretch( Vector().Max() * value_t(0.01) );
	}

	
	template <typename VecType>
	inline bool TLine<VecType>::IsInnerPoint (const vec_t &point) const
	{
		TRect<value_t>	r;
		GetBoundingRect( r );
	
		if ( not r.IsInnerPoint( point ) )
			return false;

		value_t	a, b, c;
		GetEquation( a, b, c );
		return IsZero( a * point.x + b * point.y + c );
	}

	
	template <typename VecType>
	inline bool TLine<VecType>::IsInnerLine (const Self &line) const
	{
		TRect<value_t>	r;
		GetBoundingRect( r );
	
		if ( not r.IsInnerPoint( line.Begin() ) or
			 not r.IsInnerPoint( line.End() ) )
		{
			return false;
		}
		
		value_t	a, b, c;
		GetEquation( a, b, c );

		return	IsZero( a * line.X1() + b * line.Y1() + c ) and
				isZero(	a * line.X2() + b * line.Y2() + c );
	}


	template <typename VecType>
	inline bool TLine<VecType>::IntersectLine (const Self &other) const
	{
		vec_t	res;
		return GetLineIntersection( other, res );
	}

	
	template <typename VecType>
	inline bool TLine<VecType>::IntersectRect (const TRect<value_t> &other) const
	{
		vec_t	res0, res1;
		return _IntersectRect( other, res0, res1 );
	}
	
		
	template <typename VecType>
	inline bool TLine<VecType>::IsPointInBoundingRects (const Self &other, const vec_t &point) const
	{
		TRect<value_t>	r0;
		TRect<value_t>	r1;
		this->GetBoundingRect( r0 );
		other.GetBoundingRect( r1 );
		return r0.IsInnerPoint( point ) and r1.IsInnerPoint( point );
	}

	
	template <typename VecType>
	inline bool TLine<VecType>::GetVecIntersection (const Self &other, OUT vec_t &result) const
	{
		result = vec_t();
		return GetRayIntersection( other, result );
	}


	template <typename VecType>
	inline bool TLine<VecType>::GetLineIntersection (const Self &other, OUT vec_t &result) const
	{
		result = vec_t();
		return GetRayIntersection( other, result ) and IsPointInBoundingRects( other, result );
	}
	

	template <typename VecType>
	inline bool TLine<VecType>::GetRectIntersection (const TRect<value_t> &other, OUT vec_t &res0, OUT vec_t &res1) const
	{
		return _IntersectRect( other, res0, res1 );
	}


	template <typename VecType>
	inline bool TLine<VecType>::GetRayIntersection (const Self &other, OUT vec_t &res) const
	{
		const value_t	x3  = other.X1();
		const value_t	x4  = other.X2();
		const value_t	y3  = other.Y1();
		const value_t	y4  = other.Y2();

		const value_t	x12 = X1() - X2();
		const value_t	y12 = Y1() - Y2();

		const value_t	x34 = x3   - x4;
		const value_t	y34 = y3   - y4;

		const value_t	c = x12 * y34 - y12 * x34;

		if ( IsZero( c ) )
		{
			return false;
		}
		else
		{
			const value_t	a = X1() * Y2() - Y1() * X2();
			const value_t	b = x3 * y4 - y3 * x4;
			res.x = ( a * x34 - b * x12 ) / c;
			res.y = ( a * y34 - b * y12 ) / c;
			return true;
		}
	}
	

	template <typename VecType>
	inline bool TLine<VecType>::_IntersectRect (const TRect<value_t> &other, OUT vec_t &res0, OUT vec_t &res1) const
	{
		TRect<value_t>	r;
		GetBoundingRect( r );
		
		if ( not other.IntersectRect( r ) )
			return false;

		vec_t *		results[4]	= { &res0, &res1 };
		int			i			= 0;
		bool		res			= false;
		vec_t		tmp;

		if ( GetRayIntersection( Self( other.LeftBottom(), other.LeftTop() ), tmp ) and other.IsInnerPoint( tmp ) )
		{
			*(results[i++]) = tmp;
			res = true;
		}

		if ( GetRayIntersection( Self( other.LeftBottom(), other.RightBottom() ), tmp ) and other.IsInnerPoint( tmp ) )
		{
			*(results[i++]) = tmp;
			res = true;
		}

		if ( GetRayIntersection( Self( other.LeftTop(), other.RightTop() ), tmp ) and other.IsInnerPoint( tmp ) )
		{
			*(results[i++]) = tmp;
			res = true;
		}

		if ( GetRayIntersection( Self( other.RightBottom(), other.RightTop() ), tmp ) and other.IsInnerPoint( tmp ) )
		{
			*(results[i++]) = tmp;
			res = true;
		}
		
		return res;
	}
	

	template <typename VecType>
	inline bool TLine<VecType>::_CropLine (const Self &src, Self &dst)
	{
		vec_t	i;

		if ( src.GetLineIntersection( dst, i ) )
		{
			dst.Begin() = i;
			return true;
		}
		return false;
	}
	
		
	template <typename VecType>
	inline typename VecType::value_t TLine<VecType>::AngleBetweenLines (const Self &sLine) const
	{
		const vec_t	v0 = Vector();
		const vec_t	v1 = sLine.Vector();

		if ( All( v0 == v1 ) )
			return value_t(0);

		if ( All( v0 == -v1 ) )
			return Pi<value_t>();

		const value_t	a0 = ATan( v0.x, v0.y );
		const value_t	a1 = ATan( v1.x, v1.y );
		const value_t a  = (a1 - a0);
		
		// TODO: use TRadians
		if ( a < -Pi<value_t>() )
			return a + Pi<value_t>() * value_t(2);
		
		if ( a > Pi<value_t>() )
			return a - Pi<value_t>() * value_t(2);
		
		return a;
	}
	

	template <typename VecType>
	inline typename VecType::value_t TLine<VecType>::CosBetweenLines (const Self &sLine) const
	{
		const vec_t	v0 = Vector();
		const vec_t	v1 = sLine.Vector();

		if ( All( v0 == v1 ) )
			return value_t(1);	// cos(0)

		if ( All( v0 == -v1 ) )
			return value_t(-1);	// cos(Pi)

		return Dot( v0, v1 ) / ( v0.Length() * v1.Length() );
	}

		
	template <typename VecType>
	inline TLine<VecType>  TLine<VecType>::Reflection (const vec_t &normal) const
	{
		/*      this
			|  /
			| /
			|*---------* normal
			| \
			|  \
		        * result
		*/

		return Self( End(), End() + Vector().Reflect( normal ).SetLength( Length() ) );
	}



//--------------------------- TRect -------------------------------//
	
	template <typename T>
	inline bool TRect<T>::IsInnerLine (const TLine< TVec<T,2> > &sLine) const
	{
		return IsInnerPoint( sLine.Begin() ) & IsInnerPoint( sLine.End() );
	}
	

	template <typename T>
	inline bool TRect<T>::IntersectLine (const TLine< TVec<T,2> > &sLine) const
	{
		return sLine.IntersectRect( *this );
	}
	
	
	template <typename T>
	inline bool TRect<T>::GetLineIntersection (const TLine< TVec<T,2> > &sLine, OUT TVec<T,2> &result0, OUT TVec<T,2> &result1) const
	{
		return sLine.GetRectIntersection( *this, result0, result1 );
	}
	
	
	template <typename T>
	inline bool TRect<T>::CropLine (TLine< TVec<T,2> > &sLine) const
	{
		if ( IntersectLine( sLine ) )
		{
			TODO("");
			return true;
		}
		return false;
	}
	
		
	template <typename T>
	inline TLine< TVec<T,2> >  TRect<T>::Left () const
	{
		return line_t( LeftBottom(), LeftTop() );
	}
	
	
	template <typename T>
	inline TLine< TVec<T,2> >  TRect<T>::Right () const
	{
		return line_t( RightTop(), RightBottom() );
	}
	
	
	template <typename T>
	inline TLine< TVec<T,2> >  TRect<T>::Bottom () const
	{
		return line_t( RightBottom(), LeftBottom() );
	}
		

	template <typename T>
	inline TLine< TVec<T,2> >  TRect<T>::Top () const
	{
		return line_t( LeftTop(), RightTop() );
	}
	
	
	template <typename T>
	inline TLine< TVec<T,2> >  TRect<T>::ToLine (EEdge i) const
	{
		return line_t( (*this)(i & 3), (*this)((i+1) & 3) );
	}

//-------------------------------------------------------------------

}	// UXMath
}	// UX_STL

#endif	// _UXS_LINE_H